package ac.il.technion.c236369.hw4.typehandlers.tsp.templates;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;

import org.apache.log4j.Logger;

import ac.il.technion.c236369.hw4.core.utils.FindClassFileByNameLoader;

/**
 * @author Artem Barger, Maxim Aizenshtein.
 * 
 * Parse, compile and instantiate tsp template class
 * from given tsp file.
 *
 */
public class TSPTemplateLoader {
	
	private TSPTemplate _instance;
	private File tmpFile;
	private Logger log = Logger.getLogger( TSPTemplateLoader.class);

	public TSPTemplateLoader( final File file) {
		parse( file);
		compile( );
		try {
			FindClassFileByNameLoader loader = new FindClassFileByNameLoader( );
			_instance = (TSPTemplate)loader.loadClass( tmpFile.getName().split( "\\.")[0]).newInstance( );
			if ( log.isDebugEnabled( )) {
				log.debug( "Instanciated class: <" + _instance.getClass().getName() +">");
			}
		} catch (Exception e) {
			log.error(e);
		} finally {
			// Remove class file.
			if ( _instance != null) {
				new File( tmpFile.getName().split( "\\.")[0] + ".class").delete();
			}
		}
	}


	private void parse(final File tspFile) {
		BufferedWriter writer = null;
		try {
			tmpFile = File.createTempFile( "tsp", ".java", new File("."));
			StringBuilder builder = new StringBuilder( );
			builder.append( "import java.io.PrintStream;\n");
			builder.append( "import java.util.Map;\n");
			builder.append( "import ac.il.technion.c236369.hw4.typehandlers.tsp.templates.*;\n");
			builder.append( "public class " + tmpFile.getName( ).split( "\\.")[0] + " implements TSPTemplate {\n");
			builder.append( "\n");
			builder.append( "\tpublic void process( PrintStream out, Map<String, String> params) {");
			builder.append( "\n");
			builder.append( "\t\tStringBuilder builder = new StringBuilder( );\n");
			BufferedReader reader = new BufferedReader( new FileReader( tspFile));
			parseTSPFile( builder, reader);
			builder.append( "\t\tout.flush( );\n");
			builder.append( "\t}\n"); //close function
			builder.append( "}\n"); //close class definition
			writer = new BufferedWriter( new FileWriter( tmpFile));
			writer.append( builder.toString( ));
			writer.flush( );
		} catch (IOException e) {
			log.error(e);
		} finally {
			try {
				if ( writer != null)
					writer.close( );
			} catch (IOException e) {
				log.error(e);
			}
		} 
	}


	private void parseTSPFile( StringBuilder builder, BufferedReader reader)
			throws IOException {
		String line = null;
		/**
		 * Read from tsp file and echo all special characters. 
		 */
		boolean codeSection = false;
		while( ( line = reader.readLine( )) != null) {
			String $ = line
				.replaceAll( "\"", "\\\"");
			String[] before = $.split( "<%");
			String[] after = $.split( "%>");
			if ( !codeSection)
				builder.append( "\t\tout.println(\"" + before[0] + "\");\n");
			else {
				builder.append( "\t\t" + after[0] + "\n");
				if ( after.length > 1) {
					builder.append( "\t\tout.println(\"" + after[1].split( "<%")[0] + "\");\n");
					codeSection = false;
				}
			}
			if ( before.length > 1) {
				builder.append( "\t\t" + before[1].split( "%>")[0] + "\n");
				codeSection = true;
			} else if ( $.contains( "<%") && !codeSection)
				codeSection = true;
			if ( after.length > 1) {
				builder.append( "\t\tout.println(\"" + after[1].split( "<%")[0] + "\");\n");
				codeSection = false;
			} else if ( $.contains( "%>") && codeSection )
				codeSection = false;
		}
	}
	
	private void compile() {
	    try {
			Process compileProc = Runtime.getRuntime().exec("javac -classpath bin/ " + tmpFile.getAbsolutePath( ));    
			compileProc.waitFor(); 
			
		    if(compileProc.exitValue()!=0) {
		        System.out.println("Compile exit status: " + compileProc.exitValue());
		        System.out.println("Compile error:");
		        printStream(compileProc.getErrorStream());
		    }
		} catch (IOException e) {
			log.error(e);
		} catch (InterruptedException e) {
			log.error(e);
		} finally {
			tmpFile.delete( );
		}
	}

	public static void printStream(InputStream stream) throws IOException {
		byte read = 0;
		while ((read = (byte) stream.read()) >= 0) {
			System.err.print((char) read);
		}
	}
	    
	public TSPTemplate getInstance() {
		return _instance;
	}
}
